﻿using System;
using System.Data.Common;
using System.Data;
using System.Web;
using System.Text;
using Library.OrmEnterpriseEntity.TableInfo;

namespace Library.OrmEnterpriseEntity.Factor {
	///<summary>
	/// Base dbcontext
	///</summary>
	public abstract class BaseDbContext : IDbContext {
		
		//public virtual bool BuildExpression(string filterExpression, DbType dbType, out object parameter, out QueryOperator queryOperator) {

		//  parameter = null;
		//  queryOperator = QueryOperator.Equal;

		//  return false;
		//}

		[ThreadStatic]
		private static short _currentRowType;
		private const string Key = "Library.OrmEnterpriseEntiry.Factor.CurrentRowType";
		public abstract int ExcuteStatement(DeleteStatementWrapper statement);
		public abstract int ExcuteStatement(UpdateStatementWrapper statement);
		public short GlobalRowType { get; set; }
		#region Implementation of IServiceContext

		/// <summary>
		/// RowType per thread
		/// </summary>
		/// <exception cref="EnterpriseEntityException">Web session is null</exception>
		public virtual short CurrentRowType {
			get {
				var context = HttpContext.Current;
				if (context != null) {
					if (context.Session == null) {
						throw new EnterpriseEntityException("Web session is null when excuting query in not cross row entity");
					}
					object value = context.Session[Key];
					if (value == null) return GlobalRowType;
					return Convert.ToInt16(value);
				}
				if (_currentRowType != 0)
					return _currentRowType;
				return GlobalRowType;
			}
			set {
				var context = HttpContext.Current;
				if (context != null) {
					if (context.Session == null) {
						throw new EnterpriseEntityException("Web session is null when setting current row type");
					}
					context.Session[Key] = value;
				} else {
					//LocalDataStoreSlot slot = Thread.GetNamedDataSlot(Key);
					//Thread.SetData(slot, value);
					_currentRowType = value;
				}
			}
		}

		public RowTypeChangable ChangeRowType(short rowType) {
			var old = CurrentRowType;
			CurrentRowType = rowType;
			var change = new RowTypeChangable();
			change.Disposed += delegate { CurrentRowType = old; };
			return change;
		}

		private IStringResource _englighResource;
		public virtual IStringResource StringResource {
			get {
				if (_englighResource == null) _englighResource = new EnglighResource();
				return _englighResource;
			}
		}

		public abstract bool DoInsert(GlobalEntity entity);
		public abstract bool DoUpdate(GlobalEntity entity);
		public abstract bool DoDelete(GlobalEntity entity);
		public abstract string BuildSqlWildcard(string windowWildCard);

		#endregion

		#region Implementation of IConnectionCreator

		public abstract DbConnection CreateConnection();
		public abstract DbConnection CreateConnection(string connectionString);
		/// <summary>
		/// Default readcommitted level
		/// </summary>
		public virtual IsolationLevel DefaultLevel {
			get { return IsolationLevel.ReadCommitted; }
		}

		public abstract DbDataReader ExcuteStatement(SelectStatementWrapper selectStatement);


		#endregion

		protected virtual void Excute(string commandText, object[] values, string[] names, DbType[] types, CommandType commandType) {
			new ContextService(this).Excute(commandText, values, names, types, commandType);
		}

		protected virtual object ExcuteScalar(string commandText, object[] values, string[] names, DbType[] types, CommandType commandType) {
			return new ContextService(this).ExcuteObj(commandText, values, names, types, commandType);
		}

		protected virtual object ExcuteScalar(string commandText, System.Collections.Generic.IEnumerable<QueryParameter> parameters, CommandType commandType) {
			return new ContextService(this).ExcuteObj(commandText, parameters, commandType);
		}

		protected virtual DbDataReader ExcuteReader(string commandText, object[] values, string[] names, DbType[] types, CommandType commandType, CommandBehavior behavior) {
			return new ContextService(this).ExcuteReader(commandText, values, names, types, commandType, behavior);
		}

		protected virtual DbDataReader ExcuteReader(string commandText, System.Collections.Generic.IEnumerable<QueryParameter> parameters, CommandType commandType, CommandBehavior behavior) {
			return new ContextService(this).ExcuteReader(commandText, parameters, commandType, behavior);
		}

		internal class ContextService : BaseService {
			public ContextService(IConnectionCreator dbContext)
				: base(dbContext) {
			}

			public void Excute(string commandText, object[] values, string[] names, DbType[] types, CommandType commandType) {
				ExcuteNonQuery(commandText, values, names, types, commandType);
			}
			public void Excute(string commandText, System.Collections.Generic.IEnumerable<QueryParameter> parameters, CommandType commandType) {
				ExcuteNonQuery(commandText, parameters, commandType);
			}
			public object ExcuteObj(string commandText, object[] values, string[] names, DbType[] types, CommandType commandType) {
				return ExcuteScalar(commandText, values, names, types, commandType);
			}
			public object ExcuteObj(string commandText, System.Collections.Generic.IEnumerable<QueryParameter> parameters, CommandType commandType) {
				return ExcuteScalar(commandText, parameters, commandType);
			}
			public DbDataReader ExcuteReader(string commandText, object[] values, string[] names, DbType[] types, CommandType commandType, CommandBehavior behavior) {
				return ExcuteDataReader(commandText, values, names, types, commandType, behavior);
			}
			public DbDataReader ExcuteReader(string commandText, System.Collections.Generic.IEnumerable<QueryParameter> parameters, CommandType commandType, CommandBehavior behavior) {
				return ExcuteDataReader(commandText, parameters, commandType, behavior);
			}
		}

		private class EnglighResource : IStringResource {
			#region Implementation of IStringResource

			public string MissingColumnValue(DbColumn column, string columnName, object columnValue, string tableName) {
				return String.Format("Missing value in column {0} on table {1}", columnName, tableName);
			}

			public string ColumnValidateFail(DbColumn column, string columnName, object columnValue, string tableName) {
				return String.Format("Column {0} in table {1} validates fail", columnName, tableName);
			}

			public string ColumnValueExist(DbColumn column, string columnName, object columnValue, string tableName) {
				return String.Format("Value {0} of column {1} in table {2} exist", column.DbValue, columnName, tableName);
			}

			public string ColumnValueExist(DbColumn[] columns, string[] columnNames, object[] columnValues, string tableName) {
				if (columns == null || columnNames == null || columnValues == null) return String.Empty;
				if (columns.Length != columnNames.Length || columns.Length != columnValues.Length) {
					return String.Empty;
				}
				var builder = new StringBuilder();
				for (int i = 0; i < columns.Length; i++) {
					builder.AppendFormat("Value {0} of {1}, ", columnValues[i], columnNames[i]);
				}
				builder.Append("existed");
				return builder.ToString();
			}

			public string ColumnMaxLenReach(DbColumn column, string columnName, object columnValue, string tableName) {
				return String.Format("Value length of column {0} in table {1} is over", columnName, tableName);
			}

			public string ColumnValueNotExist(DbColumn column, string columnName, object columnValue, string tableName) {
				return String.Format("Value {0} not found", columnName);
			}

			public string ColumnValueNotExist(DbColumn[] columns, string[] columnNames, object[] columnValues, string tableName) {
				var builder = new StringBuilder();
				builder.Append("Value ");
				foreach (var columnName in columnNames) {
					builder.AppendFormat("[{0}] ", columnName);
				}
				builder.Append("not found");
				return builder.ToString();
			}

			public string DeleteTransExist(DbColumn[] columns, string[] columnNames, object[] columnValues, string tableName) {
				return String.Format("Delete fail because transaction exist in table {0}", tableName);
			}

			public string DeleteTransExist(DbColumn column, string columnName, object columnValue, string tableName) {
				return String.Format("Delete fail because transaction exist in table {0}", tableName);
			}

			#endregion
		}
	}
}
